Software piracy is a problem of enormous proportions for commercial software developers. According to an ongoing study undertaken by the Business Software Alliance (BSA) to measure the extent and effects of software piracy, the world piracy rate in 2002 was 39%. This figure reflects the estimated percentage of copies of business software installed without a license. In North America the piracy rate was 24%, the lowest in the world. In Vietnam, the piracy rate was a staggering 95%, and in China it was 92%. The global losses to software developers due to piracy was estimated at $13.08 billion. $2.3 billion of this amount is attributable to piracy in North America.
Many solutions have been implemented in response to the piracy problem. Often, security features are designed into software modules to discourage, thwart, or increase the difficulty of pirating. A software module, or module, is a collection of routines and data structures that performs a particular task or implements a particular tool for use by a computer. As used here, a module can be anything from a single-task software unit to a many-task software unit. For example, a module could range from part of a software application to an entire application, or part of an operating system or an entire operating system. One frequently employed security feature instructs a module to disable its host software application if the application is not registered with its developer within a reasonable amount of time. Such security features that are designed into software modules, as well as any other security feature designed to prevent software piracy, are referred to as “security processor code.” Security processor code can be altered or disabled, however, by reverse engineering and either removing the security processor code from a module or disabling it. Therefore, in the effort to reduce piracy, it is desirable to detect and prevent the alteration of software modules.
Such detection of module alteration is made more difficult by the fact that software modules may be changed by legitimate use. However, there are usually parts of software modules that will not change with legitimate use, and detection can focus on such parts to determine if a software module has been tampered with. A software module that has been tampered with by changing binaries that should not change with the legitimate use of the software module is said to be of compromised integrity. Performing a security check to determine if a software module is of compromised integrity may be referred to as checking the integrity of a software module, or checking if a module is compromised.
One method of preventing such module tampering involves digitally signing a module and including periodically checking the signature. The procedure for signing modules warrants brief discussion. FIG. 1A and FIG. 1B illustrate the salient concepts corresponding to this description. With reference to exemplary Module 25 in FIG. 1A, software modules, when compiled, comprise strings of bits, the familiar electronic zeros and ones. Each string of bits represents a binary number. Note that for simplicity of illustration Module 25 does not show separation between the strings representing discrete binary numbers. The term “binaries” refers to two or more binary numbers, which may be either the entire set or some subset of the binary numbers that are stored in computer memory. The binaries associated with a software module represent the coded commands written and compiled by a module developer. Changes to the binaries of modules, such as the exemplary change to Module 25, are sometimes referred to as “binary patching,” because attackers frequently do not have the source code for modules and therefore make changes directly to the stored binaries. Binary patching is the replacement of some of the binaries in a module with new binaries intended to change some function of the original module.
Signing a module, such as Module 25, often involves hashing all or a selected subset of the module binaries. Hashing refers to generating a unique number that is based on the selected binaries, usually using special cryptographic techniques to generate a unique signature or fingerprint of an input binary stream. A property of hashing is that it is “collision resistant”—it is very difficult to find another binary stream which will generate the same hash value and corresponding signature after the hash function is applied to it. The hashing function which is applied to the input binary stream is usually also called a “one way,” or “trap door” function and the signature is also called a “cryptographic checksum.”
In a simplified version of hashing, the “ones” of selected binaries in a module may be added together to generate a unique number. The resulting unique number is associated with a developer's original version of a module. The hash result is sometimes called a “checksum.” Importantly, a checksum can be stored and kept with the module. A software developer can hash a module and sign the resulting checksum, for instance, using a his or her private key in a public/private key system. FIG. 1A points out an exemplary checksum that has been encrypted using a developer's private key and included in Module 25. This exemplary checksum was created prior to the binary patch inserted into Module 25 by an attacker, and therefore contains a number that would not be computed if the hash was performed on patched Module 25 as shown in FIG. 1A.
Public/private key pairs are generated through asymmetric encryption. Checksums encrypted with a private key can only be decrypted using a public key, and vice versa. If a software developer signs a checksum using a private key, he or she can be sure that only those in possession of the public key will be able to read the checksum. Users who decrypt the signed checksum with the public key can be sure it was signed by the developer, because only the developer had the private key. Many computer users today are familiar with the situation in which they are asked to enter a key number when installing modules on computing devices. The public key number is typically provided on the packaging of the module.
The user, or, more appropriately, an operating system (“OS”) running on a user's computer, can check the developer's signed checksum to verify that a module is a developer's original version. This process is illustrated in the flowchart of FIG. 1B. First, the user's OS can compute its own checksum for a software module by executing the same hash technique as the developer. An exemplary result of such a computation is provided in FIG. 1B. The user's OS can then decrypt the developer's signed checksum using a public key. Such decryption could yield the exemplary result provided in FIG. 1B. Finally, the user's OS could compare the computed checksum against the developer's signed checksum. If the user's OS computed a checksum that matches the developer's signed checksum, the module most likely has the same binaries that it had when the developer signed it. In other words, the module has not been tampered with, because any change to the module instructions, such as disabling security features, would also have changed the associated binaries. If a module has been tampered with, as shown in FIG. 1A, the checksums will not match. The module may be designed to disable itself or take other protective measures if tampering is discovered.
Operating systems, such as the familiar MICROSOFT® WINDOWS family of operating systems, may execute the process described above with regard to modules in the OS itself, or on behalf of application modules running on the OS. An OS that provides such security features allows software developers to focus more on the functional features of the modules they develop, and less on the task of independently generating security processor code for preventing piracy. In this regard, an OS can periodically calculate checksums for applications and other modules, and validate those checksums against the developer's signed checksums. If the checksums match, the OS can proceed with normal operation, but if they do not match, the OS may be designed to take protective measures.
The process of ensuring the integrity of module binaries, in general, is referred to in the industry as module authentication. It may also be referred to here as a security check. One drawback of present module authentication techniques is that, like other security features designed into applications, it can be reverse engineered and disabled. Consider, for example, FIG. 1A, and imagine that Module 25 is an OS performing module authentication on one of its modules. If the module authentication feature for Module 25 was represented by the very binaries that were patched, then no module authentication would occur. This would leave attackers free to continue to hunt down and disable other security processor code protecting Module 25, because further changes to the binaries would go undetected.
In the case of an OS that performs module authentication on behalf of application modules, disabling module authentication in the operating system could successfully circumvent piracy protection for all applications that relied on the OS for periodic module authentication. This situation is illustrated in FIG. 2. “ModuleAuthenticator.exe” is security processor code in an OS that is designed to perform module authentication for the various modules running on the OS, which may include OS modules. Each arrow to a module represents a module authentication as described with reference to FIG. 1B. Note that the strategic placement of a binary patch in an OS such as that of FIG. 2 can block all module authentications performed by ModuleAuthenticator.exe. As sophisticated as techniques may be for guarding against attacks on such an OS module authentication design, the fact remains that when such a centralized system is compromised, any module that relied on the OS for security support is also compromised.
Likewise, centralized module authentication systems are more susceptible to relatively easy reverse engineering. This is because, once found, the binaries that represent a module authentication function can be patched without further search or concern that the patch will be detected. The task of finding the module authentication binaries may be arduous, but need be conducted only once. Moreover, the search for binaries representing the module authentication function is limited to binaries of the operating system, which reduces the field of search required of would-be attackers.
In light of the above deficiencies in module authentication techniques, there is an a need in the industry to raise the bar in software protection techniques used to prevent binary patching and maintaining the integrity of the security processor code.